package com.dtp.core.spring;

import com.dtp.common.ApplicationContextHolder;
import com.dtp.core.support.ExecutorWrapper;
import com.dtp.core.DtpRegistry;
import com.dtp.core.support.DynamicTp;
import com.dtp.core.support.TaskQueue;
import com.dtp.core.thread.DtpExecutor;
import com.dtp.core.thread.EagerDtpExecutor;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;

import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.core.type.MethodMetadata;
import org.springframework.lang.NonNull;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * Dtp的后置处理器
 *
 * @author yanhom
 * @since 1.0.0
 *
 * @remark muse
 **/
@Slf4j
public class DtpPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        // 步骤1：如果bean不是ThreadPoolExecutor类型也不是ThreadPoolTaskExecutor类型，不处理直接返回
        if (!(bean instanceof ThreadPoolExecutor) && !(bean instanceof ThreadPoolTaskExecutor))
            return bean;

        // 步骤2：专门处理DtpExecutor类型线程池的代码逻辑
        if (bean instanceof DtpExecutor) {
            DtpExecutor dtpExecutor = (DtpExecutor) bean;
            if (bean instanceof EagerDtpExecutor)
                ((TaskQueue) dtpExecutor.getQueue()).setExecutor((EagerDtpExecutor) dtpExecutor);

            registerDtp(dtpExecutor); // 注册DtpExecutor
            return dtpExecutor;
        }

        // 步骤3：解析@DynamicTp中配置的线程池名称
        ApplicationContext applicationContext = ApplicationContextHolder.getInstance();
        String dtpAnnotationVal;
        try {
            DynamicTp dynamicTp = applicationContext.findAnnotationOnBean(beanName, DynamicTp.class); // 获得@DynamicTp注解信息
            if (Objects.nonNull(dynamicTp)) {
                dtpAnnotationVal = dynamicTp.value(); // 获得线程池名称
            } else {
                BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext;
                BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
                if (!(beanDefinition instanceof AnnotatedBeanDefinition)) return bean;

                AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
                MethodMetadata methodMetadata = (MethodMetadata) annotatedBeanDefinition.getSource();
                if (Objects.isNull(methodMetadata) || !methodMetadata.isAnnotated(DynamicTp.class.getName())) return bean;

                dtpAnnotationVal = Optional.ofNullable(methodMetadata.getAnnotationAttributes(DynamicTp.class.getName()))
                        .orElse(Collections.emptyMap())
                        .getOrDefault("value", "")
                        .toString();
            }
        } catch (NoSuchBeanDefinitionException e) {
            log.error("There is no bean with the given name {}", beanName, e);
            return bean;
        }

        String poolName = StringUtils.isNotBlank(dtpAnnotationVal) ? dtpAnnotationVal : beanName; // 获得线程池名称

        // 步骤4：注册一个普通的线程池
        if (bean instanceof ThreadPoolTaskExecutor)
            registerCommon(poolName, ((ThreadPoolTaskExecutor) bean).getThreadPoolExecutor());
        else
            registerCommon(poolName, (ThreadPoolExecutor) bean);

        return bean;
    }

    /** 注册一个DtpExecutor（仅仅维护到DTP_REGISTRY缓存中）*/
    private void registerDtp(DtpExecutor executor) {
        DtpRegistry.registerDtp(executor, "beanPostProcessor");
    }

    /** 注册一个普通线程池（仅仅维护到COMMON_REGISTRY缓存中）*/
    private void registerCommon(String poolName, ThreadPoolExecutor executor) {
        ExecutorWrapper wrapper = new ExecutorWrapper(poolName, executor);
        DtpRegistry.registerCommon(wrapper, "beanPostProcessor");
    }
}
